#!/usr/bin/perl

##
## using the given arguments, build a list of modules.  There must be
## a .mod file for each argument
##
## this program should be run in the current directory.
##
#
# Note to module developers: the MAGIC_MODNUMBER is generated every time
# this program is run.  Since it is stored in the clean file of the binary
# db you cannot rebuild the modules and use the same binary db without
# dumping the binary db to text.  To specify your own magic number run
# this program with the argument '-m' followed by a number.
#

sub die {
    print "@_";
    `rm -f moddef.h`;
    exit(0);
}

$nmods = 0;
@mods = ();
$magic = time();
$write=1;

@args = @ARGV;
@arg_save = ();

if ($#args == -1) {
    open(TMP, "modbuild.last");
    $tmp = <TMP>;
    chop($tmp);
    close(TMP);
    @args = split(/ /, $tmp);
}

doargs:
for ($x=0; $x <= $#args; $x++) {
    $_ = $args[$x];
    if (/^-m(.*)/) {
        ## ohh, a special magic number!
        $rest = $1;
        if (length($rest)) {
            $magic = $rest;
        } else {
            $x++;
            $magic = $args[$x];
        }
        if ($magic !~ /[0-9]+/) {
            &die("Invalid magic number \"$magic\"\n");
        }
        push(@arg_save, "-m$magic");
    } else {
        $nmods++;
        push(@mods, $_);
        push(@arg_save, $_);
    }
}

$arg_save = join(" ", @arg_save);

`echo $arg_save > modbuild.last`;

if ($write) {
## open moddef.h now, add to it as we go
open(DEF, ">moddef.h") || die("Unable to open moddef.h");
print DEF <<END;
/*
// Full copyright information is available in the file ../doc/CREDITS
*/

#ifndef _moddef_h_
#define _moddef_h_

#include "native.h"
#include "native.h"

END

foreach $mod (@mods) {
    print DEF "#include \"$mod.h\"\n";
}

print DEF<<END;

#define NUM_MODULES $nmods

#ifdef _native_
module_t * cold_modules[] = {
END

foreach $mod (@mods) {
    print DEF "    \&${mod}_module,\n";
}

print DEF "};\n#endif\n\n";
}

## now frob each module file
$struct = "";
$defn = 0;
$objects = "";
foreach $mod (@mods) {
    if (!(-f "./$mod.mod")) {
        print "No module found by the name of $mod.mod\n";
        next;
    }
    open(MOD, "$mod.mod");

    while (<MOD>) {
        chop;
        (/^\s*#/ || /^\s*$/) && next;
        s/\s+/ /g;
        s/^\s*//g;
        s/\s*$//g;
        if (/^objs\s*(.*)$/) {
            $objects .= " " . $1;
            next;
        } elsif (/^native\s*(.*)$/) {
            $_ = $1;
        } else {
            print "Ignoring unknown directive \"$_\".\n";
            next;
        }
        ($ref, $func) = split(/\s+/, $_);
        ($ref !~ /\./) && &die("Object reference \"$ref\" is invalid.\n");
        ($obj, $name) = split(/\./, $ref);
        ($obj =~ /^#/) &&
            &die("Native methods may only be specified with object names.\n");
        $obj =~ s/^\$//;
        ($obj =~ /[^a-z0-9_]/) &&
            &die("Object name \"$obj\" contains invalid characters.\n");
        $name =~ s/\(\s*\)//g;
        ($name =~ /[^a-z0-9_]/) &&
            &die("Native method name \"$name\" contains invalid characters.\n");
        $def = $name;
        $def =~ tr/[a-z]/[A-Z]/;
        $defo = $obj;
        $defo =~ tr/[a-z]/[A-Z]/;
        if ($func !~ /^native_/) { $func = "native_$func"; }
        print DEF "#define NATIVE_${defo}_${def} $defn\n";
        $buf = sprintf("    {%-15s %-20s %s},\n",
                "\"$obj\",", "\"$name\",", "$func");
        $struct .= $buf;
        $defn++;
    }
}

if ($write) {
    print DEF <<END;
#define NATIVE_LAST $defn

#define MAGIC_MODNUMBER $magic\n

#ifdef _native_
native_t natives[NATIVE_LAST] = {
$struct};
#else
extern native_t natives[NATIVE_LAST];
#endif

#endif
END

    close(DEF);
}

$objs = "";
for (split(/\s+/, $objects)) {
    s/\s+/ /g;
    s/^\s*//g;
    s/\s*$//g;
    (!$_) && next;

    !(/^modules\//) && ($_ = "modules/$_");
    print "$_ ";
}

print "\n";


